/*
 * Copyright (c) 2006-2021, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2022-07-25     yuanzihao    init
 */
#include <stdio.h>
#include <board.h>
#include <flashdb.h>
#include <flashdb_app.h>
#include <rtthread.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>

static struct rt_semaphore flashdb_lock;

static uint32_t boot_count = 0;
static uint32_t pomodoro_count = 0; // 完成的番茄钟计数
/* default KV nodes */
static struct fdb_default_kv_node default_kv_table[] = {
	{"SSID", "", 0},											 /* string KV */
	{"PASSWORD", "", 0},										 /* string KV */
	{"boot_count", &boot_count, sizeof(boot_count)},			 /* int type KV */
	{"pomodoro_count", &pomodoro_count, sizeof(pomodoro_count)}, /* int type KV */
};
/* KVDB object */
struct fdb_kvdb kvdb = {0};
extern void kvdb_basic_sample(fdb_kvdb_t kvdb);

static void lock(fdb_db_t db)
{
	rt_sem_take(&flashdb_lock, RT_WAITING_FOREVER);
}

static void unlock(fdb_db_t db)
{
	rt_sem_release(&flashdb_lock);
}

static int init_falshdb_lock(void)
{
	rt_sem_init(&flashdb_lock, "flashdb lock", 1, RT_IPC_FLAG_PRIO);
	return 0;
}
INIT_PREV_EXPORT(init_falshdb_lock);

int start_up_boot_count(void)
{
#ifdef FDB_USING_KVDB
	{ /* KVDB Sample */
		struct fdb_default_kv default_kv;

		default_kv.kvs = default_kv_table;
		default_kv.num = sizeof(default_kv_table) / sizeof(default_kv_table[0]);
		/* set the lock and unlock function if you want */
		fdb_kvdb_control(&kvdb, FDB_KVDB_CTRL_SET_LOCK, (void *)lock);
		fdb_kvdb_control(&kvdb, FDB_KVDB_CTRL_SET_UNLOCK, (void *)unlock);
		/* Key-Value database initialization
		 *
		 *       &kvdb: database object
		 *       "env": database name
		 * "kvdb": The flash partition name base on FAL. Please make sure it's in FAL partition
		 * table. Please change to YOUR partition name. &default_kv: The default KV nodes. It will
		 * auto add to KVDB when first initialize successfully. NULL: The user data if you need, now
		 * is empty.
		 */
		fdb_err_t result;
		result = fdb_kvdb_init(&kvdb, "database", "FlashDB", &default_kv, NULL);

		if (result != FDB_NO_ERR)
		{
			return -1;
		}

		/* run basic KV samples */
		kvdb_basic_sample(&kvdb);
	}

#endif /* FDB_USING_KVDB */
	return 0;
}
INIT_APP_EXPORT(start_up_boot_count);

void inc_pomodoro_count(void)
{
	struct fdb_blob blob;

	{ /* GET the KV value */
		/* get the "pomodoro_count" KV value */
		fdb_kv_get_blob(&kvdb, "pomodoro_count", fdb_blob_make(&blob, &pomodoro_count, sizeof(pomodoro_count)));
		/* the blob.saved.len is more than 0 when get the value successful */
		if (blob.saved.len > 0)
		{
			log_i("get the 'pomodoro_count' value is %d\n", pomodoro_count);
		}
		else
		{
			log_i("get the 'pomodoro_count' failed\n");
		}
	}

	{ /* CHANGE the KV value */
		/* increase the boot count */
		pomodoro_count++;
		/* change the "pomodoro_count" KV's value */
		fdb_kv_set_blob(&kvdb, "pomodoro_count", fdb_blob_make(&blob, &pomodoro_count, sizeof(pomodoro_count)));
		log_i("set the 'pomodoro_count' value to %d\n", pomodoro_count);
	}
}

int get_pomodoro_count(void)
{
	struct fdb_blob blob;
	fdb_kv_get_blob(&kvdb, "pomodoro_count", fdb_blob_make(&blob, &pomodoro_count, sizeof(pomodoro_count)));
	/* the blob.saved.len is more than 0 when get the value successful */
	if (blob.saved.len > 0)
	{
		log_i("get the 'pomodoro_count' value is %d\n", pomodoro_count);
	}
	else
	{
		log_i("get the 'pomodoro_count' failed\n");
	}
	return pomodoro_count;
}

bool isHexNumStr(const char *pStr)
{
	int i;
	for (i = 0; i < strlen(pStr); i++)
	{
		if (!isxdigit(pStr[i]))
		{
			return false;
		}
	}
	return true;
}

bool isDecNumStr(const char *pStr)
{
	int i = 0;
	if (pStr[0] == '-')
	{
		i = 1;
	}
	for (; i < strlen(pStr); i++)
	{
		if (!isdigit(pStr[i]))
		{
			return false;
		}
	}
	return true;
}

bool isFloatNumStr(const char *pStr)
{
	int i = 0;
	bool found_dot = 0;
	if (pStr[0] == '-')
	{
		i = 1;
	}
	for (; i < strlen(pStr); i++)
	{
		if (!found_dot && pStr[i] == '.')
		{
			found_dot = 1;
			continue;
		}
		if (!isdigit(pStr[i]))
		{
			return false;
		}
	}
	return true;
}

void flashdb_set_key_value(char *argv, uint32_t value)
{
	struct fdb_blob blob;
	char temp[20];
	strcpy(temp, argv);
	const char *tempptr = temp;
	/* change the "temp_uint32_count" KV's value */
	fdb_kv_set_blob(&kvdb, tempptr, fdb_blob_make(&blob, &value, sizeof(value)));
	log_i("set the '%s' value to %d", temp, value);
}
void flashdb_get_key_value(char *argv)
{
	uint32_t temp_uint32_count;
	struct fdb_blob blob;
	char temp[20];
	strcpy(temp, argv);
	const char *tempptr = temp;
	/* get the "temp_uint32_count" KV value */
	fdb_kv_get_blob(&kvdb,
					tempptr,
					fdb_blob_make(&blob, &temp_uint32_count, sizeof(temp_uint32_count)));
	/* the blob.saved.len is more than 0 when get the value successful */
	if (blob.saved.len > 0)
	{
		log_i("'%s': %d\n", temp, temp_uint32_count);
	}
	else
	{
		log_i("Failed to get '%s'!", temp);
	}
}

void flashdb_set_key_string(char *string, char *str)
{
	fdb_kv_set(&kvdb, string, str);
	log_i("'%s' <=  %s", string, str);
}
void flashdb_get_key_string(char *string)
{
	char *return_value, temp_data[100];

	/* Get the "temp" KV value.
	 * NOTE: The return value saved in fdb_kv_get's buffer. Please copy away as soon as
	 * possible.
	 */
	return_value = fdb_kv_get(&kvdb, string);
	/* the return value is NULL when get the value failed */
	if (return_value != NULL)
	{
		strncpy(temp_data, return_value, sizeof(temp_data));
		log_i("'%s': %s\n", string, temp_data);
	}
	else
	{
		log_i("Failed to get '%s'!", string);
	}
}

char *flashdb_get_key_string_return(char *string)
{
	static char *return_value, temp_data[100];

	/* Get the "temp" KV value.
	 * NOTE: The return value saved in fdb_kv_get's buffer. Please copy away as soon as
	 * possible.
	 */
	return_value = fdb_kv_get(&kvdb, string);
	/* the return value is NULL when get the value failed */
	if (return_value != NULL)
	{
		strncpy(temp_data, return_value, sizeof(temp_data));
		log_i("'%s': %s\n", string, temp_data);
	}
	else
	{
		log_i("Failed to get '%s'!", string);
	}
	return return_value;
}

static int flashdb(int argc, char **argv)
{
	int ret = 0;
	if (argc < 2)
	{
		goto ERROR;
	}
	if (!rt_strcmp(argv[1], "init"))
	{
		//		flashdb_init();
	}
	else if (!rt_strcmp(argv[1], "kvdb_type_string_sample"))
	{
		//		kvdb_type_string_sample(&kvdb);
	}
	else if (!rt_strcmp(argv[1], "kvdb_basic_sample"))
	{
		//		kvdb_basic_sample(&kvdb);
	}
	else if (!rt_strcmp(argv[1], "get"))
	{
		if (argc < 3)
		{
			ret = -1;
			goto ERROR;
		}
		if (!rt_strcmp(argv[2], "value"))
		{
			flashdb_get_key_value(argv[3]);
		}
		else if (!rt_strcmp(argv[2], "string"))
		{
			flashdb_get_key_string(argv[3]);
		}
		else
		{
			ret = -2;
			goto ERROR;
		}
	}
	else if (!rt_strcmp(argv[1], "set"))
	{
		if (argc < 4)
		{
			ret = -1;
			goto ERROR;
		}
		if ((!rt_strcmp(argv[2], "value")) && (isDecNumStr(argv[4])))
		{
			flashdb_set_key_value(argv[3], atoi(argv[4]));
		}
		else if (!rt_strcmp(argv[2], "string"))
		{
			flashdb_set_key_string(argv[3], argv[4]);
		}
		else
		{
			ret = -2;
			goto ERROR;
		}
	}
	return ret;
ERROR:
	log_e("Usage: FlashDB "
		  "[init|get|set]"
		  "[value|string]"
		  "[(number)|(string)]");
	return ret;
}
MSH_CMD_EXPORT(flashdb, flashdb_control
			   : flashdb<init | get | set><value | string><(number) | (string)>);
//--- Exported functions --------------------------------------------------------------

#ifdef __cplusplus
extern "C"
{
#endif

#ifdef __cplusplus
}
#endif
